package albert3d.gui;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Locale;
import java.util.Scanner;
import javax.media.opengl.GL3;
import com.jogamp.opengl.util.GLBuffers;

public class ObjLoader {

	private GL3 gl;
	public FloatBuffer vertices = null;
	public IntBuffer faces = null;
	public FloatBuffer normals = null;
	public IntBuffer normalIndex = null;
	
	public void load(GL3 gl3, float[] vertexData, float[] normalData, int[] faceData) {
		if(vertexData != null) {
			vertices = GLBuffers.newDirectFloatBuffer(vertexData.length);
			for(int i=0; i<vertexData.length; i++) {
				vertices.put(vertexData[i]);
			}
			vertices.rewind();
		}
		
		if(normalData != null) {
			normals = GLBuffers.newDirectFloatBuffer(normalData.length);
			for(int i=0; i<normalData.length; i++) {
				normals.put(normalData[i]);
			}
			normals.rewind();
		}
		
		if(faceData != null) {
			faces = GLBuffers.newDirectIntBuffer(faceData.length);
			for(int i=0; i<faceData.length; i++) {
				faces.put(faceData[i]);
			}
			faces.rewind();
		}
		
		printInfo("DEFAULT", false);
	}

	public void loadFile(GL3 gl3, String fileName) throws IOException {
		try {
			gl = gl3;
			InputStream is = new FileInputStream(new File(fileName));
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			
			int vertDataSize = getVerticesDataSize(fileName);
			int normDataSize = getNormalsDataSize(fileName);
			int faceDataSize = getFacesDataSize(fileName);
			
			if(vertDataSize > 0)
				vertices = GLBuffers.newDirectFloatBuffer(vertDataSize);
			if(normDataSize > 0)
				normals = GLBuffers.newDirectFloatBuffer(normDataSize);
			if(faceDataSize > 0)
				faces = GLBuffers.newDirectIntBuffer(faceDataSize);
			if(faceDataSize > 0)
				normalIndex = GLBuffers.newDirectIntBuffer(faceDataSize);
			
			String str = null;
			while ((str = br.readLine()) != null) {
				Scanner scan = new Scanner(str);
				scan.useLocale(Locale.US);

				if (str.startsWith("v ") && vertices != null) {
					scan.next();
					vertices.put(scan.nextFloat());
					vertices.put(scan.nextFloat());
					vertices.put(scan.nextFloat());
				}
				
				if (str.startsWith("vn ") && normals != null) {
					scan.next();
					normals.put(scan.nextFloat());
					normals.put(scan.nextFloat());
					normals.put(scan.nextFloat());
				}

				if (str.startsWith("f ") && faces != null && str.contains("//")) {
					scan.next();
					int i = -1;
					String getStr = scan.next();
					String[] nums = getStr.split("//");
					faces.put(Integer.parseInt(nums[0])+i); normalIndex.put(Integer.parseInt(nums[1])+i);
					getStr = scan.next();
					nums = getStr.split("//");
					faces.put(Integer.parseInt(nums[0])+i); normalIndex.put(Integer.parseInt(nums[1])+i);
					getStr = scan.next();
					nums = getStr.split("//");
					faces.put(Integer.parseInt(nums[0])+i); normalIndex.put(Integer.parseInt(nums[1])+i);
				}
			}
			
			//printInfo(fileName, false);
			
			if(vertices!=null)
				vertices.rewind();
			if(faces!=null)
				faces.rewind();
			if(normals!=null)
				normals.rewind();
			if(normalIndex!=null)
				normalIndex.rewind();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	public int readVertexBuffer() {
		int vertexData = 0;
		if(vertices != null) {
			IntBuffer intBuffer = GLBuffers.newDirectIntBuffer(1);
			gl.glGenBuffers(1, intBuffer);
			vertexData = intBuffer.get(0);
			gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vertexData);
			gl.glBufferData(GL3.GL_ARRAY_BUFFER, vertices.capacity() * (Float.SIZE/8), vertices, GL3.GL_DYNAMIC_DRAW);
		}
		return vertexData;
	}
	
	public int readNormalBuffer() {
		int normalData = 0;
		if(normals != null) {
			IntBuffer intBuffer = GLBuffers.newDirectIntBuffer(1);
			gl.glGenBuffers(1, intBuffer);
			normalData = intBuffer.get(0);
			gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, normalData);
			gl.glBufferData(GL3.GL_ARRAY_BUFFER, normals.capacity() * (Float.SIZE/8), normals, GL3.GL_DYNAMIC_DRAW);
		}
		return normalData;
	}
	
	public int readIndexBuffer() {
		int faceData = 0;
		if(faces != null) {
			IntBuffer intBuffer = GLBuffers.newDirectIntBuffer(1);
			gl.glGenBuffers(1, intBuffer);
			faceData = intBuffer.get(0);
			gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, faceData);
			gl.glBufferData(GL3.GL_ELEMENT_ARRAY_BUFFER, faces.capacity() * (Integer.SIZE/8), faces, GL3.GL_DYNAMIC_DRAW);
		}
		return faceData;
	}
	
	public void render() {
		if(vertices != null) {
			if(faces != null)
				gl.glDrawElements(GL3.GL_TRIANGLES, faces.capacity(), GL3.GL_UNSIGNED_INT, 0);
			else
				gl.glDrawArrays(GL3.GL_POINTS, 0, vertices.capacity()/3);
		}
	}

	private int getVerticesDataSize(String fileName) throws IOException {

		int i = 0;
		String str = null;
		InputStream is = new FileInputStream(new File(fileName));
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		while ((str = br.readLine()) != null) {
			if (str.startsWith("v "))
				++i;
		}

		return i * 3;
	}

	private int getFacesDataSize(String fileName) throws IOException {

		int i = 0;
		String str = null;
		InputStream is = new FileInputStream(new File(fileName));
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		while ((str = br.readLine()) != null) {
			if (str.startsWith("f "))
				++i;
		}

		return i * 3;
	}
	
	private int getNormalsDataSize(String fileName) throws IOException {
		int i = 0;
		String str = null;
		InputStream is = new FileInputStream(new File(fileName));
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		while ((str = br.readLine()) != null) {
			if (str.startsWith("vn "))
				++i;
		}

		return i * 3;
	}
	
	public void rotate(int angleX, int angleY, int angleZ) {
		ByteBuffer bb = gl.glMapBuffer(GL3.GL_ARRAY_BUFFER, GL3.GL_READ_WRITE);
		FloatBuffer fb = bb.order(ByteOrder.nativeOrder()).asFloatBuffer();
		
		
		float[] tab = new float[fb.capacity()];
		for(int i=0; i<vertices.capacity(); i++) {
			tab[i] = vertices.get(i);
		}
		
		vertices.rewind();
		
		for(int i=0; i<fb.capacity(); i+=3) {
			fb.put(tab[i]*(float)Math.cos(Math.toRadians(angleX))-tab[i+1]*(float)Math.sin(Math.toRadians(angleX)));
			fb.put(tab[i]*(float)Math.sin(Math.toRadians(angleX))+tab[i+1]*(float)Math.cos(Math.toRadians(angleX)));
			fb.put(tab[i+2]);
		}
		
		fb.rewind();
		
		tab = new float[fb.capacity()];
		for(int i=0; i<fb.capacity(); i++) {
			tab[i] = fb.get(i);
		}
		
		fb.rewind();
		
		for(int i=0; i<fb.capacity(); i+=3) {
			fb.put(tab[i]);
			fb.put(tab[i+1]*(float)Math.cos(Math.toRadians(angleY))-tab[i+2]*(float)Math.sin(Math.toRadians(angleY)));
			fb.put(tab[i+1]*(float)Math.sin(Math.toRadians(angleY))+tab[i+2]*(float)Math.cos(Math.toRadians(angleY)));
		}
		
		tab = new float[fb.capacity()];
		for(int i=0; i<fb.capacity(); i++) {
			tab[i] = fb.get(i);
		}
		
		fb.rewind();
		
		for(int i=0; i<fb.capacity(); i+=3) {
			fb.put(tab[i+2]*(float)Math.sin(Math.toRadians(angleZ))+tab[i]*(float)Math.cos(Math.toRadians(angleZ)));
			fb.put(tab[i+1]);
			fb.put(tab[i+2]*(float)Math.cos(Math.toRadians(angleZ))-tab[i]*(float)Math.sin(Math.toRadians(angleZ)));
		}
		
		fb.rewind();
		
		gl.glUnmapBuffer(GL3.GL_ARRAY_BUFFER);
	}
	
	public void scale(float scaleX, float scaleY, float scaleZ) {
		ByteBuffer bb = gl.glMapBuffer(GL3.GL_ARRAY_BUFFER, GL3.GL_READ_WRITE);
		FloatBuffer fb = bb.order(ByteOrder.nativeOrder()).asFloatBuffer();
		
		float[] tab = new float[fb.capacity()];
		for(int i=0; i<vertices.capacity(); i++) {
			tab[i] = vertices.get(i);
		}
		
		vertices.rewind();
		
		for(int i=0; i<fb.capacity(); i+=3) {
			fb.put(tab[i]*scaleX);
			fb.put(tab[i+1]*scaleY);
			fb.put(tab[i+2]*scaleZ);
		}
		
		fb.rewind();
		
		gl.glUnmapBuffer(GL3.GL_ARRAY_BUFFER);
	}
	
	private void printInfo(String fileName, boolean vertexData) {
		System.out.println("+------------------------------------------+");
		System.out.println("| OBJECT: " + new File(fileName).getName());
		
		if(vertices != null) {
			System.out.println("|   vertices: " + vertices.capacity()/3);
			if(vertexData == true) {
				for (int i = 0, x = 0; i < vertices.capacity(); i += 3, x++) {
					System.out.println(x + " - " + "x: " + vertices.get(i) + "y: "
							+ vertices.get(i + 1) + "z: " + vertices.get(i + 2));
				}
			}
		}
		
		if(normals != null) {
			System.out.println("|   normals:  " + normals.capacity()/3);
			if(vertexData == true) {
				for (int i = 0, x = 0; i < normals.capacity(); i += 3, x++) {
					System.out.println(x + " - " + "x: " + normals.get(i) + "y: "
							+ normals.get(i + 1) + "z: " + normals.get(i + 2));
				}
			}
		}
		
		if(faces != null) {
			System.out.println("|   faces:   " + faces.capacity()/3);
			if(vertexData == true) {
				for (int i = 0, x = 0; i < faces.capacity(); i += 3, x++) {
					System.out.println(x + " - " + "x: " + faces.get(i) + "y: "
							+ faces.get(i + 1) + "z: " + faces.get(i + 2));
				}
			}
		}
		
		System.out.println("+------------------------------------------+");
	}
	
	public String[] getInfo(String fileName) {
		String[] s = new String[4];
		s[0] = new String("OBJECT: " + new File(fileName).getName());
		if(vertices!=null)
			s[1] = new String("    vertices: " + vertices.capacity()/3);
		if(normals!=null)
			s[2] = new String("    normals:  " + normals.capacity()/3);
		if(faces!=null)
			s[3] = new String("    faces:   " + faces.capacity()/3);
		return s;
	}

}